package ncme.auto.framework.core.upload.service;

import ncme.auto.framework.core.entities.config.ConfigBean;
import ncme.auto.framework.core.upload.entity.MultipartFileParam;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.OverlappingFileLockException;
import java.text.DecimalFormat;
import java.util.UUID;

@Service
public class ChunkUploadServiceImpl {

    @Autowired
    private ConfigBean configBean;

    /**
     * 分块上传
     * 第一步：获取RandomAccessFile,随机访问文件类的对象
     * 第二步：调用RandomAccessFile的getChannel()方法，打开文件通道 FileChannel
     * 第三步：获取当前是第几个分块，计算文件的最后偏移量
     * 第四步：获取当前文件分块的字节数组，用于获取文件字节长度
     * 第五步：使用文件通道FileChannel类的 map（）方法创建直接字节缓冲器  MappedByteBuffer
     * 第六步：将分块的字节数组放入到当前位置的缓冲区内  mappedByteBuffer.put(byte[] b);
     * 第七步：释放缓冲区
     * 第八步：检查文件是否全部完成上传
     *
     * @param param 文件流参数
     * @param folderName 文件夹名
     * @return "exp:"+param.getChunkNumber()
     * @throws IOException 文件读取异常
     */
    public String chunkUploadByMappedByteBuffer(MultipartFileParam param, String folderName) throws IOException {
        if (param.getIdentifier() == null || "".equals(param.getIdentifier())) {
            param.setIdentifier(UUID.randomUUID().toString());
        }
        String fileName = param.getFilename();
        String filePath = configBean.getNetworkDiskPath()+folderName+"\\";
        File fileDir = new File(filePath);
        if (!fileDir.exists()) {
            fileDir.mkdirs();
        }

        RandomAccessFile raf = null;

        try {
            File tempFile = new File(filePath, fileName);
            //第一步
            raf = new RandomAccessFile(tempFile, "rw");
            long offset = (param.getChunkNumber() - 1) * param.getChunkSize();
            byte[] fileData = param.getUpfile().getBytes();
            raf.seek(offset);
            raf.write(fileData, 0, fileData.length);
            //第八步
            double prog = checkUploadStatus(param,fileName,filePath);
            if (prog==1.0)
                return "100%";
            else
            {
                String format = new DecimalFormat("#.0").format(prog*100);
                return format+'%';
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        catch (OverlappingFileLockException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("程序设计不合理，加锁区域相互重叠");

        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            close(raf);
        }
        return "exp:"+param.getChunkNumber();
    }

    /**
     * 检查文件上传进度
     * @return
     */
    private Double checkUploadStatus(MultipartFileParam param,String fileName,String filePath) throws IOException {
        File confFile = new File(filePath,fileName+".conf");
        RandomAccessFile confAccessFile = new RandomAccessFile(confFile,"rw");
        //设置文件长度
        confAccessFile.setLength(param.getTotalChunks());
        //设置起始偏移量
        confAccessFile.seek(param.getChunkNumber() - 1);
        //将指定的一个字节写入文件中 127，
        confAccessFile.write(Byte.MAX_VALUE);

        byte[] completeStatusList = FileUtils.readFileToByteArray(confFile);
        byte isComplete = Byte.MAX_VALUE;
        Integer completeProg = 0;
        for(int i = 0; i<completeStatusList.length && isComplete==Byte.MAX_VALUE; i++){
            isComplete = (byte)(isComplete & completeStatusList[i]);
            //System.out.println("check part " + i + " complete?:" + completeStatusList[i]);
            if(completeStatusList[i]==127)
                completeProg++;
        }

        close(confAccessFile);

        if(isComplete == Byte.MAX_VALUE){
            confFile.delete();
            return 1.0;
        }
        double prog = (double)completeProg / (double) param.getTotalChunks();
        return prog;
    }

    private static void close(Closeable... closeables) {
        if (closeables == null || closeables.length == 0) {
            return;
        }
        for (Closeable closeable : closeables) {
            if (closeable == null) {
                continue;
            }
            try {
                closeable.close();
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 分块上传分块存储
     *
     * 第一步：获取RandomAccessFile,随机访问文件类的对象
     * 第二步：调用RandomAccessFile的getChannel()方法，打开文件通道 FileChannel
     * 第三步：获取当前是第几个分块，计算文件的最后偏移量
     * 第四步：获取当前文件分块的字节数组，用于获取文件字节长度
     * 第五步：使用文件通道FileChannel类的 map（）方法创建直接字节缓冲器  MappedByteBuffer
     * 第六步：将分块的字节数组放入到当前位置的缓冲区内  mappedByteBuffer.put(byte[] b);
     * 第七步：释放缓冲区
     * 第八步：检查文件是否全部完成上传
     * @param param 文件流参数
     * @param folderName 文件夹名
     * @return "exp:"+param.getChunkNumber()
     * @throws IOException 文件读取异常
     */
    @Deprecated
    public String chunkUploadByMappedByteBufferSave(MultipartFileParam param, String folderName) throws IOException {
        if (param.getIdentifier() == null || "".equals(param.getIdentifier())) {
            param.setIdentifier(UUID.randomUUID().toString());
        }

        String fileName = param.getFilename();
        //String fileName = param.getUpfile().getOriginalFilename();
        String tempFileName = fileName + "_chunk_" + param.getChunkNumber();
        String filePath = configBean.getNetworkDiskPath()+folderName+"\\";
        File fileDir = new File(filePath);
        if (!fileDir.exists()) {
            fileDir.mkdirs();
        }

        RandomAccessFile raf = null;

        try {
            File tempFile = new File(filePath, tempFileName);
            //第一步
            raf = new RandomAccessFile(tempFile, "rw");
            //第二步
            //fileChannel = raf.getChannel();
            //第三步
            long offset = (param.getChunkNumber() - 1) * param.getChunkSize();
            //第四步
            byte[] fileData = param.getUpfile().getBytes();
            raf.seek(0);
            raf.write(fileData, 0, fileData.length);

            //第八步
            double prog = checkUploadStatus(param,fileName,filePath);
            if (prog==1.0)
                return "100%";
            else
            {
                String format = new DecimalFormat("#.0").format(prog*100);
                return format+'%';
            }

            //boolean isComplete = false;
//            if (isComplete) {
//                renameFile(tempFile, fileName);
//            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        catch (OverlappingFileLockException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("程序设计不合理，加锁区域相互重叠");
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            close(raf);
        }
        return "exp:"+param.getChunkNumber();
    }
}
